package com.nuoniu.sibanyun.service.production.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nuoniu.sibanyun.common.constant.RedisConstant;
import com.nuoniu.sibanyun.common.exception.NuoNiuException;
import com.nuoniu.sibanyun.common.lang.StringUtils;
import com.nuoniu.sibanyun.entity.bo.VProductionWorkDetailCumBo;
import com.nuoniu.sibanyun.entity.bo.VProductionWorkInventoryDetailCumBo;
import com.nuoniu.sibanyun.entity.erp.ErpNumberSet;
import com.nuoniu.sibanyun.entity.inOut.VInOutRecordDetail;
import com.nuoniu.sibanyun.entity.inOut.VInOutRecordMain;
import com.nuoniu.sibanyun.entity.po.VPurchaseInvoiceMain;
import com.nuoniu.sibanyun.entity.production.*;
import com.nuoniu.sibanyun.entity.so.VSaleDeliveryDetail;
import com.nuoniu.sibanyun.entity.so.VSaleDeliveryMain;
import com.nuoniu.sibanyun.mapper.erp.ErpNumberSetMapper;
import com.nuoniu.sibanyun.mapper.production.VProductionPickReturnMainMapper;
import com.nuoniu.sibanyun.service.crm.ICrmGoodsService;
import com.nuoniu.sibanyun.service.erp.IErpAccountService;
import com.nuoniu.sibanyun.service.erp.IErpNumberSetService;
import com.nuoniu.sibanyun.service.erp.in.IInWarehouseService;
import com.nuoniu.sibanyun.service.inOut.IVInOutRecordMainService;
import com.nuoniu.sibanyun.service.production.*;
import com.nuoniu.sibanyun.service.redis.RedisService;
import com.nuoniu.sibanyun.service.utils.ErpUtils;
import com.nuoniu.sibanyun.userThread.UserInfo;
import com.nuoniu.sibanyun.userThread.UserThreadLocal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 生产领退补料单主表 服务实现类
 * </p>
 *
 * @author 小啊刚
 * @since 2021-01-15
 */
@Service
public class VProductionPickReturnMainServiceImpl extends ServiceImpl<VProductionPickReturnMainMapper, VProductionPickReturnMain> implements IVProductionPickReturnMainService {

    @Autowired
    private IErpAccountService accountService;
    @Autowired
    private IVProductionPickReturnDetailService productionPickReturnDetailService;
    @Autowired
    private ErpNumberSetMapper numberSetMapper;
    @Autowired
    private IErpNumberSetService numberSetService;
    @Autowired
    private RedisService redisService;
    @Autowired
    private IInWarehouseService warehouseService;

    @Autowired
    private IVProductionDispatchMainService productionDispatchMainService;
    @Autowired
    private IVInOutRecordMainService ivInOutRecordMainService;
    @Autowired
    private IVProductionWorkMainService productionWorkMainService;
    @Autowired
    private ICrmGoodsService crmGoodsService;


    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void saveOrder(VProductionPickReturnMain productionPickReturnMain) {
        UserInfo userInfo = UserThreadLocal.get();
        Integer currentAccountId = accountService.getCurrentAccountId();
        Long mainId = productionPickReturnMain.getId();
        //业务类型 4:领料5:退料6:补料
        Integer businessType = productionPickReturnMain.getBusinessType();
        List<VProductionPickReturnDetail> orderDetailList = productionPickReturnMain.getOrderDetailList();
//        if((5 == businessType) && orderDetailList.stream().anyMatch(f->f.getBaseQuantity().compareTo(BigDecimal.ZERO) > 0)){
//            throw new NuoNiuException("单据明细错误：退货业务的明细数量不能为正数");
//        }
        String dataSource = productionPickReturnMain.getDataSource();
        if(mainId == null) {
            //如果 数据来说 是 委外领料
            if("DelegateRRS".equals(dataSource)){
                String codeForDelegate = getCodeForDelegate();
                productionPickReturnMain.setVoucherCode(codeForDelegate);
            }else{
                productionPickReturnMain.setDataSource("RRS");
                String code = getCode();
                if(code == null) {
                    new NuoNiuException("单据编号已上限");
                }
                productionPickReturnMain.setVoucherCode(code);
            }
            productionPickReturnMain.setAccountId(currentAccountId);
            productionPickReturnMain.setCompanyId(userInfo.getCompanyId());
            productionPickReturnMain.setCreateUserName(userInfo.getRealName());
            productionPickReturnMain.setCreateUserId(userInfo.getUserId());
            productionPickReturnMain.setVoucherState(-1);
        }else {
            VProductionPickReturnMain oldVProductionPickReturnMain = getById(mainId);
            productionPickReturnMain.setUpdateUserId(userInfo.getUserId());
            productionPickReturnMain.setUpdateUserName(userInfo.getRealName());
            productionPickReturnMain.setUpdateDate(LocalDateTime.now());
            if(!crmGoodsService.isHaveOperationRight(oldVProductionPickReturnMain.getCreateUserId().longValue())){
                throw new NuoNiuException("你没有权限操作该记录");
            }
            Integer voucherState = oldVProductionPickReturnMain.getVoucherState();
            if (voucherState > 0)throw new NuoNiuException("该单据已经审核，不能修改");
        }
        // 校验 明细数量
//        if (5 == productionPickReturnMain.getBusinessType()){
//            boolean isOk = productionPickReturnMain.getOrderDetailList().stream().anyMatch(f -> null == f.getBaseQuantity() || !(f.getBaseQuantity().compareTo(BigDecimal.ZERO) < 0));
//            if(isOk)throw new NuoNiuException("明细数量必须小于0");
//        }else{
//            boolean isOk = productionPickReturnMain.getOrderDetailList().stream().anyMatch(f -> null == f.getBaseQuantity() || !(f.getBaseQuantity().compareTo(BigDecimal.ZERO) > 0));
//            if(isOk)throw new NuoNiuException("明细数量必须大于0");
//        }
        boolean isOk = productionPickReturnMain.getOrderDetailList().stream().anyMatch(f -> null == f.getBaseQuantity() || !(f.getBaseQuantity().compareTo(BigDecimal.ZERO) > 0));
        if(isOk)throw new NuoNiuException("明细数量必须大于0");
        //计算 明细物料 总数
        BigDecimal totalQuantity =  productionPickReturnMain.getOrderDetailList().stream().map(VProductionPickReturnDetail::getBaseQuantity).reduce(BigDecimal.ZERO,BigDecimal::add);
        productionPickReturnMain.setTotalQuantity(totalQuantity.abs());
        Boolean result = this.saveOrUpdate(productionPickReturnMain);
        if(result) { //保存明细
            List<Long> detailIds = new ArrayList<>();

            orderDetailList.forEach(p -> {
                p.setCompanyId((userInfo.getCompanyId()));
                p.setAccountId(currentAccountId);
                p.setOrderId(productionPickReturnMain.getId().intValue());
                if(p.getId() != null) {
                    detailIds.add(p.getId());
                }
            });

            boolean isDealDelete = false;
            if (null != mainId){
                //删除明细
                QueryWrapper wrapper = new QueryWrapper();
                wrapper.eq("order_id", mainId);
                if(detailIds != null && detailIds.size() > 0) {
                    wrapper.notIn("id", detailIds);
                }
                isDealDelete = productionPickReturnDetailService.remove(wrapper);
            }

            // *** 如果 来源订单不为空,校验
            if(StringUtils.isNotBlank(productionPickReturnMain.getSourceVoucherType())){
                if(isDealDelete)throw new NuoNiuException("有来源的单据不能删除明细");
                if(! ("ProductionWork".equals(productionPickReturnMain.getSourceVoucherType()) || "DelegateProductionWork".equals(productionPickReturnMain.getSourceVoucherType())   ))throw new NuoNiuException("领退补(料)单据的来源单，只能是自制加工单/委外加工单");
                //如果是编辑，先删除 累计执行情况
                if (null != mainId ){
                    //获取单据明细
                    QueryWrapper<VProductionPickReturnDetail> oldWrapper = new QueryWrapper();
                    oldWrapper.eq("order_id", productionPickReturnMain.getId());
                    List<VProductionPickReturnDetail> oldOrderDetailList = productionPickReturnDetailService.list(oldWrapper);
                    //删除 操作
                    calculateCumQuantity(oldOrderDetailList,businessType,2);
                }
                //保存 操作
                calculateCumQuantity(orderDetailList,businessType,1);
            }
            //保存单据明细表
            productionPickReturnDetailService.saveOrUpdateBatch(orderDetailList);
        }
    }

    /**
     * 组装 累计执行 的材料领退补数据
     * @param orderDetailList
     * @param businessType
     * @param calculateType
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void calculateCumQuantity(List<VProductionPickReturnDetail> orderDetailList, Integer businessType, Integer calculateType) {
        List<VProductionWorkDetailCumBo> productionWorkDetailCumBoList = new ArrayList<>();
        HashMap<Integer,VProductionWorkDetailCumBo> productionWorkDetailCumBoHashMap = new HashMap<>();
        orderDetailList.forEach(orderDetail->{
            Integer sourceProductionWorkId = orderDetail.getSourceProductionWorkId();
            Integer sourceWorkInventoryDetailId = orderDetail.getSourceWorkInventoryDetailId();
            VProductionWorkDetailCumBo productionWorkDetailCumBo = productionWorkDetailCumBoHashMap.get(sourceProductionWorkId);
            if (null == productionWorkDetailCumBo){
                productionWorkDetailCumBo = new VProductionWorkDetailCumBo();
                productionWorkDetailCumBo.setProductionWorkId(sourceProductionWorkId);
                productionWorkDetailCumBo.setBusinessType(businessType);
                productionWorkDetailCumBo.setCalculateType(calculateType);
                productionWorkDetailCumBo.setProductionWorkInventoryDetailCumBoHashMap(new HashMap<>());
                productionWorkDetailCumBoHashMap.put(sourceProductionWorkId,productionWorkDetailCumBo);
                productionWorkDetailCumBoList.add(productionWorkDetailCumBo);
            }
            HashMap<Long, VProductionWorkInventoryDetailCumBo> productionWorkInventoryDetailCumBoHashMap = productionWorkDetailCumBo.getProductionWorkInventoryDetailCumBoHashMap();
            VProductionWorkInventoryDetailCumBo vProductionWorkInventoryDetailCumBo = new VProductionWorkInventoryDetailCumBo();
            vProductionWorkInventoryDetailCumBo.setCumQuantity(orderDetail.getBaseQuantity().abs());
            productionWorkInventoryDetailCumBoHashMap.put(sourceWorkInventoryDetailId.longValue(),vProductionWorkInventoryDetailCumBo);
        });
        productionWorkMainService.calculateCumQuantity(productionWorkDetailCumBoList);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void delete(Long id) {
        UserInfo userInfo = UserThreadLocal.get();
        VProductionPickReturnMain productionPickReturnMain = getById(id);
        if ( null == productionPickReturnMain ){
            throw new NuoNiuException("此单据不存在");
        }
        Integer businessType = productionPickReturnMain.getBusinessType();
        /**************  修改权限 *******************/
        if(!crmGoodsService.isHaveOperationRight(productionPickReturnMain.getCreateUserId().longValue())){
            throw new NuoNiuException("你没有权限操作该记录");
        }
        // 判断 是否生单
        BigDecimal cumExecuteQuantity = productionPickReturnMain.getCumExecuteQuantity();
        if (null != cumExecuteQuantity && BigDecimal.ZERO.compareTo(cumExecuteQuantity) > 0){
            throw new NuoNiuException("已经生单，不能删除");
        }
        QueryWrapper<VProductionPickReturnDetail> wrapper = new QueryWrapper();
        wrapper.eq("order_id", id);
        if (StringUtils.isNotBlank(productionPickReturnMain.getSourceVoucherType())){
            //获取单据明细
            List<VProductionPickReturnDetail> orderDetailList = productionPickReturnDetailService.list(wrapper);
            /************** 组装 更新 产品 明细 执行情况 数据*************/
            //删除 操作
            calculateCumQuantity(orderDetailList,businessType,2);
        }
        this.removeById(id);
        productionPickReturnDetailService.remove(wrapper);
    }


    @Override
    public String getCode() {
        UserInfo userInfo = UserThreadLocal.get();
        Integer currentAccountId = accountService.getCurrentAccountId();
        ErpNumberSet numberSet = numberSetMapper.getByCode("RRS", userInfo.getCompanyId(), currentAccountId);
        String dateStr = numberSet.getMedian() == 1 ? "%Y-%m-%d" : "%Y-%m";
        String code = baseMapper.getNewestCode(userInfo.getCompanyId(), dateStr,"RRS");
        return numberSetService.getCode(numberSet, code);
    }

    @Override
    public void submit(Long id, Integer status) {
        if(null == id || null == status || (null != status && (status > 0 || status < -1 )))throw new NuoNiuException("参数错误");
        UserInfo userInfo = UserThreadLocal.get();
        VProductionPickReturnMain main = this.getById(id);
        if( null == main)throw new NuoNiuException("记录不存在");
        //校验状态
        Integer voucherState = main.getVoucherState();
        /**  统一校验 提交状态 */
        ErpUtils.checkSubmitStatus(voucherState,status);
        //提交
        main.setVoucherState(status);
        main.setSubmitDate(LocalDateTime.now());
        main.setSubmitUserId(userInfo.getUserId());
        main.setSubmitUserName(userInfo.getRealName());
        this.updateById(main);
    }



    @Override
    public void examine(Long id, Integer status) {

        if (!(0 == status || 1 == status))throw new NuoNiuException("参数错误");
        UserInfo userInfo = UserThreadLocal.get();
        VProductionPickReturnMain oldVProductionPickReturnMain = getById(id);
        if (null == oldVProductionPickReturnMain)throw new NuoNiuException("参数错误");
        Integer voucherState = oldVProductionPickReturnMain.getVoucherState();
        if(  voucherState > 1 )throw new NuoNiuException("已经申请，不能做审核操作");
        if( 0  == voucherState && 0 == status)throw new NuoNiuException("未审核，不能做弃审操作");
        if( 1  == voucherState && 1 == status)throw new NuoNiuException("已审核，不能做审核操作");

        VProductionPickReturnMain productionPickReturnMain = new VProductionPickReturnMain();
        productionPickReturnMain.setVoucherState(status);
        productionPickReturnMain.setId(id);
        productionPickReturnMain.setReviewerUserName(userInfo.getRealName());
        productionPickReturnMain.setReviewerUserId(userInfo.getUserId());
        updateById(productionPickReturnMain);
    }

    /**
     * 领料申请（申请）
     * @param id
     * @param status
     */
    @Override
    public void apply(Long id, Integer status) {
        if (!(1 == status || 2 == status))throw new NuoNiuException("参数错误");
        UserInfo userInfo = UserThreadLocal.get();
        VProductionPickReturnMain oldVProductionPickReturnMain = getById(id);
        if (null == oldVProductionPickReturnMain)throw new NuoNiuException("参数错误");
        BigDecimal cumExecuteQuantity = oldVProductionPickReturnMain.getCumExecuteQuantity();
        if(null != cumExecuteQuantity && cumExecuteQuantity.compareTo(BigDecimal.ZERO) > 0)throw new NuoNiuException("已经生单，不能做退单操作");
        Integer voucherState = oldVProductionPickReturnMain.getVoucherState();
        if(  voucherState > 2 )throw new NuoNiuException("已经关闭，不能做申请/退单操作");
        if (1 == status){
            if( 1  == voucherState )throw new NuoNiuException("未提交，不能做退单操作");
            /****************  仓库权限校验 ——开始   ********************/
            Integer warehouseId = oldVProductionPickReturnMain.getWarehouseId();
            boolean isHaveOperationRight = warehouseService.isHaveOperationRight(warehouseId.longValue());
            if(!isHaveOperationRight)throw new NuoNiuException("你没有该仓库的没有操作权限");
            /****************  仓库权限校验 ——结束   ********************/
        }
        if( 2  == voucherState && 2 == status)throw new NuoNiuException("未审核，不能做申请操作");

        VProductionPickReturnMain productionPickReturnMain = new VProductionPickReturnMain();
        productionPickReturnMain.setVoucherState(status);
        productionPickReturnMain.setId(id);
        productionPickReturnMain.setReviewerUserName(userInfo.getRealName());
        productionPickReturnMain.setReviewerUserId(userInfo.getUserId());
        updateById(productionPickReturnMain);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void saveMyWarehouseInOut(VInOutRecordMain vInOutRecordMain) {
        String sourceVoucherType = vInOutRecordMain.getSourceVoucherType();
        String dataSource = vInOutRecordMain.getDataSource();
        if (!"RRS".equals(sourceVoucherType))throw new NuoNiuException("来源单据错误");
        if (!"MaterialDispatchVoucher".equals(dataSource))throw new NuoNiuException("目标单据数据来源错误");
        //1.校验 来源单数据明细仓库 权限
        List<VInOutRecordDetail> orderDetailList = vInOutRecordMain.getOrderDetailList();
        List<Long> myWarehouseIds = warehouseService.myWarehouseIds();
        if (null == myWarehouseIds || myWarehouseIds.size() == 0 )throw new NuoNiuException("仓库权限不足，请分配仓库权限");
        Set<Integer> warehouseIdSet = orderDetailList.stream().map(VInOutRecordDetail::getWarehouseId).collect(Collectors.toSet());
        boolean isOk = warehouseIdSet.containsAll(myWarehouseIds);
        if (!isOk)throw new NuoNiuException("仓库权限不足,请重新选择明细仓库");
        Set<Long> idSet = ivInOutRecordMainService.saveOrderForWarehouse(vInOutRecordMain);
        if (null == idSet || idSet.size() == 0)throw new NuoNiuException("保存失败");
        idSet.forEach(id-> ivInOutRecordMainService.examine(id,1));
    }

    @Override
    public boolean checkDataSourceFromInOut(VInOutRecordMain vInOutRecordMain) {
        String dataSource = vInOutRecordMain.getDataSource();
        String sourceVoucherType = vInOutRecordMain.getSourceVoucherType();
        Integer sourceVoucherId = vInOutRecordMain.getSourceVoucherId();
        List<VInOutRecordDetail> inOutDetailList = vInOutRecordMain.getOrderDetailList();
        if (!("MaterialDispatchVoucher".equals(dataSource) && "RRS".equals(sourceVoucherType)) && !("DelegateDispatchVoucher".equals(dataSource) && "DelegateRRS".equals(sourceVoucherType)) )throw new NuoNiuException("校验失败，数据来源错误");
        VProductionPickReturnMain pickReturnMain = getById(sourceVoucherId);
        List<VProductionPickReturnDetail> sourceDetailList = productionPickReturnDetailService.getSimpleDetailsByOrderId(pickReturnMain.getId());
        Map<Long,VInOutRecordDetail> inOutDetailMap = new HashMap<>();
        inOutDetailList.forEach(detail->inOutDetailMap.put(detail.getSourceDetailId().longValue(),detail));
        sourceDetailList.forEach(sourceDetail->{
            Long detailId = sourceDetail.getId();
            VInOutRecordDetail inOutDetail = inOutDetailMap.get(detailId.longValue());
            if (null == inOutDetail)throw new NuoNiuException("校验失败，来源明细数据不完整，缺失明细物料：" + sourceDetail.getInventoryName());
            if (!(null != inOutDetail.getInventoryId() && null != sourceDetail.getInventoryId()))
                throw new NuoNiuException("校验失败,明细物料数据错误");
            if (!inOutDetail.getInventoryId().equals(sourceDetail.getInventoryId()))
                throw new NuoNiuException("明细物料不一致，领料单明细物料名称：" + sourceDetail.getInventoryName() +",出库单明细物料名称：" + inOutDetail.getInventoryName());
            if (!(null != inOutDetail.getBaseQuantity() && null != sourceDetail.getBaseQuantity()))
                throw new NuoNiuException("校验失败,明细物料数据数量不可为空");
            if (inOutDetail.getBaseQuantity().compareTo(sourceDetail.getBaseQuantity()) != 0)
                throw new NuoNiuException("明细物料数量不一致，领料单明细物料名称：" + sourceDetail.getInventoryName() +"("+sourceDetail.getBaseQuantity()+"),出库单明细物料名称：" + inOutDetail.getInventoryName() +"("+inOutDetail.getBaseQuantity()+")");

        });
        return true;
    }

    @Override
    public boolean isHaveFromWorkMainId(Long workMainId) {
        return baseMapper.isHaveFromWorkMainId(workMainId);
    }


    @Override
    public String getCodeForDelegate() {
        UserInfo userInfo = UserThreadLocal.get();
        Integer currentAccountId = accountService.getCurrentAccountId();
        ErpNumberSet numberSet = numberSetMapper.getByCode("DelegateRRS", userInfo.getCompanyId(), currentAccountId);
        String dateStr = numberSet.getMedian() == 1 ? "%Y-%m-%d" : "%Y-%m";
        String code = baseMapper.getNewestCode(userInfo.getCompanyId(), dateStr,"DelegateRRS");
        return numberSetService.getCode(numberSet, code);
    }


    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void saveOrderForDelegate(VProductionPickReturnMain productionPickReturnMain) {
        Long mainId = productionPickReturnMain.getId();
        if(null == mainId){
            productionPickReturnMain.setDataSource("DelegateRRS");
        }

        this.saveOrder(productionPickReturnMain);
    }

}
